# -*- coding: utf-8 -*-

# Copyright (c) Huoty, All rights reserved
# Author: Huoty <sudohuoty@163.com>

from __future__ import print_function

import os
import sys
import re
import subprocess
from functools import partial
from contextlib import contextmanager
from tempfile import TemporaryDirectory

from IPython.display import DisplayObject

try:
    from shutil import which as find_executable
except ImportError:
    def find_executable(executable):
        for path in os.environ["PATH"].split(os.pathsep):
            exe_path = os.path.join(path, executable)
            if os.path.exists(exe_path) and os.access(exe_path, os.X_OK):
                return exe_path
        return None


__version__ = "0.1.0"


@contextmanager
def chdir(destination):
    cwd = os.getcwd()
    try:
        os.chdir(destination)
        yield
    finally:
        os.chdir(cwd)


def split_string_ignore_quotes(string):
    pattern = r'\s+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)'
    return [
        item.replace('"', '').replace("'", "")
        for item in re.split(pattern, string)
    ]


class FormattedText(DisplayObject):

    def __init__(self, text):
        self.text = text

    def _repr_mimebundle_(self, include=None, exclude=None):
        return {'text/plain': self.text}


def run_lua_code(code):
    lua_exe = find_executable('lua')
    process = subprocess.run([lua_exe, '-e', code], text=True, capture_output=True)
    if process.stderr:
        print(process.stderr, file=sys.stderr)
    return process.stdout


def ipython_lua_magic_function(line, cell=None):
    if not line and not cell:
        code = 'print("Hello, welcome to " .. _VERSION)'
    else:
        code = line or cell
    result = run_lua_code(code)
    return FormattedText(result)


def run_cpp_code(code, compiler_name='gcc', compiling_args=None):
    compiler_exe = find_executable(compiler_name)
    cmd = [compiler_exe]
    if compiling_args:
        cmd += compiling_args

    with TemporaryDirectory(prefix="ipy_cpp_magic_") as cwd:
        with chdir(cwd):
            source_file = "main.c" if compiler_name == 'gcc' else "main.cpp"
            out_file = 'main.exe'
            with open(source_file, 'w') as fp:
                fp.write(code)
            cmd += [source_file, '-o', out_file]
            process = subprocess.run(
                cmd, text=True, cwd=cwd, capture_output=True,
            )
            if process.stdout:
                print(process.stdout, file=sys.stdout)
            if process.stderr:
                print("Compilation Error:", file=sys.stderr)
                print(process.stderr, file=sys.stderr)
            if not os.path.exists(out_file):
                return

            process = subprocess.run(
                f"./{out_file}",
                text=True, cwd=cwd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT
            )
            return process.stdout


def ipython_cpp_magic_function(line, cell=None, compiler_name='gcc'):
    if not cell:
        print('error: no input source code', file=sys.stderr)
        return None
    compiling_args = line.split() if line else []
    code = cell
    if "main" not in code:
        if compiler_name in ("gcc"):
            code = "#include <stdio.h>\nint main(void) {\n%s;\n}" % code
        else:
            code = ("#include <stdio.h>\n#include <iostream>\n"
                    "int main(void) {\n%s;\n}") % code
    result = run_cpp_code(
        code, compiler_name=compiler_name, compiling_args=compiling_args
    )
    return None if result is None else FormattedText(result)


def load_ipython_extension(ipython):
    # lua language
    ipython.register_magic_function(ipython_lua_magic_function, 'line', 'lua')
    ipython.register_magic_function(ipython_lua_magic_function, 'cell', 'lua')

    # c/c++ language
    c_magic_func = partial(ipython_cpp_magic_function, compiler_name='gcc')
    ipython.register_magic_function(c_magic_func, 'cell', 'c')
    ipython.register_magic_function(c_magic_func, 'cell', 'cc')
    cpp_magic_func = partial(ipython_cpp_magic_function, compiler_name='g++')
    ipython.register_magic_function(cpp_magic_func, 'cell', 'cpp')
    clang_magic_func = partial(ipython_cpp_magic_function, compiler_name='clang')
    ipython.register_magic_function(clang_magic_func, 'cell', 'clang')
